#include "BTreeNode.h"

using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{ 

return pf.read(pid, buffer);
 }
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{ 

return pf.write(pid, buffer);
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{ 
int num_keys = 0;

for (int b = 0; b < 1024; b+=8){
if (buffer[b] == NULL || buffer[b+1] == NULL)
break;

num_keys++;
}

return num_keys;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{
int temp_compare;
char tempstore[1020];

if(getKeyCount() == 0)
{
memcpy(buffer, &key, sizeof(key));
memcpy(&buffer[sizeof(key)], &rid, sizeof(rid));
}
else{
for (int i = 0; i < getKeyCount(); i++){
memcpy(&temp_compare, &buffer[i*12],sizeof(int));

if (key < temp_compare){
memcpy(tempstore, &buffer[i*12], 12*(getKeyCount()-i));

memcpy(&buffer[i*12], &key, sizeof(key));
memcpy(&buffer[i*12+sizeof(int)], &rid, sizeof(rid));
memcpy(&buffer[i*12+12], tempstore, 12*(getKeyCount()-i));
break;
}

if (i+1 == getKeyCount()){
memcpy(&buffer[(i+1)*12], &key, sizeof(key));
memcpy(&buffer[(i+1)*12+sizeof(int)], &rid, sizeof(rid));
}

}
}

return 0; }

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{
int temp_compare; 
char temp_buff[1036];
char tempstore[1020];

memcpy(&temp_buff, &buffer, 12*getKeyCount());
//memcpy(&temp_buff, &rid, sizeof(rid));


for (int i = 0; i < getKeyCount(); i++){
memcpy(&temp_compare, &temp_buff[i*12],sizeof(int));

if (key < temp_compare){
memcpy(tempstore, &temp_buff[i*12], 12*(getKeyCount()-i));

memcpy(&temp_buff[i*12], &key, sizeof(key));
memcpy(&temp_buff[i*12+sizeof(int)], &rid, sizeof(rid));
memcpy(&temp_buff[i*12+12], tempstore, 12*(getKeyCount()-i));
break;
}
}
//BTLeafNode::insert(key, rid);

int a = 0;

for (int i = (getKeyCount() +1)/2; i < getKeyCount() +1; i++){
if (a==0)
memcpy(&siblingKey, &temp_buff[i*12], sizeof(int));

memcpy(&sibling.buffer[a*12], &temp_buff[i*12], sizeof(int));
memcpy(&sibling.buffer[a*12+4], &temp_buff[i*12+4], 8);
a++;
}

memset(&buffer[12*getKeyCount()+1/2], 0, 12*(getKeyCount()+1/2 + 1));

return 0;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{ 
int temp_key;
bool success = false;

for (int a = 0; a < getKeyCount(); a++){
memcpy(&temp_key, &buffer[a*12], sizeof(int));

if (temp_key >= searchKey){
eid = a; 
success = true;
break;
}
}
if (success)
return 0; 
else
return 1;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{
if (eid > getKeyCount() -1)
return 1;

else 
memcpy(&key, &buffer[eid*12], sizeof(int));
memcpy(&rid, &buffer[eid*12]+4, sizeof(rid));
return 0; }

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{ 
PageId next_node;

memcpy(&next_node, &buffer[1020], sizeof(next_node));

return next_node; 
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{ 

memcpy(&buffer[1020], &pid, sizeof(pid));

return 0; 
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{

return pf.read(pid, buffer);
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{ 
return pf.write(pid, buffer);
}
/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{ 

int num_keys = 0;

for (int b = 4; b < 1024; b+=8){
if (buffer[b] == NULL || buffer[b+1] == NULL)
break;

num_keys++;
}

return num_keys; 
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(PageId pid, int key)
{

char tempstore[1020];

int temp_key;

for (int i = 0; i < getKeyCount(); i++){
memcpy(&temp_key,&buffer[i*8+4],4); 

if (key < temp_key){
memcpy(tempstore, &buffer[i*8+4], 8*(getKeyCount()-i));

memcpy(&buffer[i*8], &key, sizeof(int));
memcpy(&buffer[i*8+4], &pid, sizeof(int));
memcpy(&buffer[i*8+8], tempstore, 8*(getKeyCount()-i));
break;
}

if (i+1 == getKeyCount()){
memcpy(&buffer[(i+1)*8], &key, sizeof(int));
memcpy(&buffer[(i+1)*8+4], &pid, sizeof(int));
}
}



 return 0;
 }

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{

int temp_compare; 
char temp_buff[1036];
char tempstore[1020];

memcpy(&temp_buff, &buffer, 8*getKeyCount()+4);
//memcpy(&temp_buff, &rid, sizeof(rid));

for (int i = 0; i < getKeyCount(); i++){
memcpy(&temp_compare, &temp_buff[i*8+4],4);

if (key < temp_compare){
memcpy(tempstore, &temp_buff[i*8+4], 8*(getKeyCount()-i));

memcpy(&temp_buff[i*8], &key, sizeof(int));
memcpy(&temp_buff[i*8+4], &pid, sizeof(int));
memcpy(&temp_buff[i*8+8], tempstore, 8*(getKeyCount()-i));
break;
}
}

memcpy(&midKey, &temp_buff[8*((getKeyCount()+1)/2)+4], 4);

memcpy(&sibling, &temp_buff[8*((getKeyCount()+1)/2)+8], 8*((getKeyCount()+1)/2)+4);

memset(&buffer[8*(getKeyCount()+1)/2+4], 0, 8*(getKeyCount()+1)/2+4);

return 0; 
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
int key_compare_low;
int key_compare_high;

for (int i = 0; i < getKeyCount(); i++){
if (getKeyCount()==1 || i == getKeyCount() - 1){
memcpy(&key_compare_low,& buffer[i*12+4], sizeof(int));

if (searchKey < key_compare_low){
memcpy(&pid, &buffer[i*12], sizeof(int));
break;
}
else if(searchKey > key_compare_low){
memcpy(&pid, &buffer[i*12+8], sizeof(int));
break;
}
else 
return 1;

}

else{
memcpy(&key_compare_low, &buffer[i*12+4], sizeof(int));
memcpy(&key_compare_high, &buffer[i*12+12], sizeof(int));

if (searchKey < key_compare_low){
memcpy(&pid, &buffer[i*12], sizeof(int));
break;
}
else if (searchKey >= key_compare_low && searchKey < key_compare_high){
memcpy(&pid, &buffer[i*12+8], sizeof(int));
break;
}

}

}

return 0; }

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{ 
memcpy(buffer, &pid1, 4);
memcpy(&buffer[4], &key, 4);
memcpy(&buffer[8], &pid2, 4);
return 0; }
